package com.blog.cmrpersonalblog.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.blog.cmrpersonalblog.dto.notification.requset.NotificationCreateRequest;
import com.blog.cmrpersonalblog.dto.notification.requset.NotificationQueryRequest;
import com.blog.cmrpersonalblog.dto.notification.respnose.NotificationGroupResponse;
import com.blog.cmrpersonalblog.dto.notification.respnose.NotificationResponse;
import com.blog.cmrpersonalblog.dto.notification.respnose.NotificationStatsResponse;
import com.blog.cmrpersonalblog.entity.Article;
import com.blog.cmrpersonalblog.entity.Comment;
import com.blog.cmrpersonalblog.entity.Notification;
import com.blog.cmrpersonalblog.entity.NotificationStats;
import com.blog.cmrpersonalblog.mapper.ArticleMapper;
import com.blog.cmrpersonalblog.mapper.CommentMapper;
import com.blog.cmrpersonalblog.mapper.NotificationMapper;
import com.blog.cmrpersonalblog.mapper.NotificationStatsMapper;
import com.blog.cmrpersonalblog.mapper.UserAnnouncementReadMapper;
import com.blog.cmrpersonalblog.service.NotificationCacheService;
import com.blog.cmrpersonalblog.service.NotificationService;
import com.blog.cmrpersonalblog.service.WebSocketNotificationService;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import org.springframework.data.redis.core.RedisTemplate;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 通知服务实现类
 */
@Slf4j
@Service
public class NotificationServiceImpl implements NotificationService {

    @Resource
    private NotificationMapper notificationMapper;

    @Resource
    private NotificationStatsMapper notificationStatsMapper;

    @Resource
    private UserAnnouncementReadMapper userAnnouncementReadMapper;

    @Resource
    private ArticleMapper articleMapper;

    @Resource
    private CommentMapper commentMapper;

    @Resource
    private WebSocketNotificationService webSocketNotificationService;

    @Resource
    private com.blog.cmrpersonalblog.service.NotificationSettingsService notificationSettingsService;

    @Resource
    private NotificationCacheService notificationCacheService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public IPage<NotificationResponse> getNotificationPage(Long userId, NotificationQueryRequest query) {
        log.info("分页查询通知列表: userId={}, query={}", userId, query);
        try {
            Page<NotificationResponse> page = new Page<>(query.getCurrent(), query.getSize());
            IPage<NotificationResponse> result = notificationMapper.selectNotificationPage(page, userId, query);

            // 批量填充关联数据
            batchFillNotificationData(result.getRecords(), userId);

            return result;
        } catch (Exception e) {
            log.error("分页查询通知列表失败: userId={}", userId, e);
            throw new RuntimeException("查询通知列表失败", e);
        }
    }

    @Override
    public NotificationResponse getNotificationDetail(Long notificationId, Long userId) {
        log.info("获取通知详情: notificationId={}, userId={}", notificationId, userId);
        try {
            NotificationResponse response = notificationMapper.selectNotificationDetail(notificationId, userId);
            if (response == null) {
                throw new RuntimeException("通知不存在或无权访问");
            }
            return response;
        } catch (Exception e) {
            log.error("获取通知详情失败: notificationId={}", notificationId, e);
            throw new RuntimeException("获取通知详情失败", e);
        }
    }

    @Override
    public NotificationStatsResponse getNotificationStats(Long userId) {
        log.info("获取用户通知统计: userId={}", userId);
        try {
            // 优化：使用缓存获取统计信息
            NotificationStatsResponse response = notificationCacheService.getNotificationStats(userId);
            
            // 如果缓存中没有统计信息，初始化
            if (response == null || response.getUserId() == null) {
                initUserNotificationStats(userId);
                response = notificationCacheService.getNotificationStats(userId);
            }

            // 查询公告未读数量
            try {
                // 判断用户角色，确定目标受众
                String targetAudience = "user"; // 默认为普通用户
                try {
                    if (cn.dev33.satoken.stp.StpUtil.hasRole("admin")) {
                        targetAudience = "admin";
                    }
                } catch (Exception e) {
                    // 如果获取角色失败，使用默认值
                    log.debug("获取用户角色失败，使用默认值: user");
                }

                int unreadAnnouncementCount = userAnnouncementReadMapper.countUnreadAnnouncements(userId, targetAudience);
                response.setUnreadAnnouncementCount(unreadAnnouncementCount);

                // 更新总未读数量（包含公告）
                Integer originalTotal = response.getTotalUnreadCount() != null ? response.getTotalUnreadCount() : 0;
                response.setTotalUnreadCount(originalTotal + unreadAnnouncementCount);

                // 更新是否有未读标识
                response.setHasUnread(response.getTotalUnreadCount() > 0);

                log.debug("公告未读数量: userId={}, targetAudience={}, count={}", userId, targetAudience, unreadAnnouncementCount);
            } catch (Exception e) {
                log.error("查询公告未读数量失败: userId={}", userId, e);
                // 公告未读数查询失败不影响整体返回，设置为0
                response.setUnreadAnnouncementCount(0);
            }

            return response;
        } catch (Exception e) {
            log.error("获取用户通知统计失败: userId={}", userId, e);
            throw new RuntimeException("获取通知统计失败", e);
        }
    }

    @Override
    public List<NotificationResponse> getLatestNotifications(Long userId, Integer limit) {
        log.info("获取最新通知列表: userId={}, limit={}", userId, limit);
        try {
            if (limit == null || limit <= 0) {
                limit = 10;
            }
            if (limit > 50) {
                limit = 50;
            }
            return notificationMapper.selectLatestNotifications(userId, limit);
        } catch (Exception e) {
            log.error("获取最新通知列表失败: userId={}", userId, e);
            throw new RuntimeException("获取最新通知失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean markAsRead(Long notificationId, Long userId) {
        log.info("标记通知为已读: notificationId={}, userId={}", notificationId, userId);
        try {
            int result = notificationMapper.markAsRead(notificationId, userId);
            
            // 优化：更新缓存，减少未读数
            if (result > 0) {
                notificationCacheService.decrementUnreadCount(userId, 1);
                // 清除统计信息缓存，下次查询时重新计算
                notificationCacheService.clearUserCache(userId);
                // 清除聚合通知缓存
                clearGroupedNotificationCache(userId);
            }
            
            return result > 0;
        } catch (Exception e) {
            log.error("标记通知为已读失败: notificationId={}", notificationId, e);
            throw new RuntimeException("标记通知为已读失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchMarkAsRead(List<Long> notificationIds, Long userId) {
        log.info("批量标记通知为已读: notificationIds={}, userId={}", notificationIds, userId);
        try {
            if (notificationIds == null || notificationIds.isEmpty()) {
                return false;
            }
            int result = notificationMapper.batchMarkAsRead(notificationIds, userId);
            
            // 优化：更新缓存，减少未读数
            if (result > 0) {
                notificationCacheService.decrementUnreadCount(userId, result);
                // 清除统计信息缓存
                notificationCacheService.clearUserCache(userId);
                // 清除聚合通知缓存
                clearGroupedNotificationCache(userId);
            }
            
            return result > 0;
        } catch (Exception e) {
            log.error("批量标记通知为已读失败: userId={}", userId, e);
            throw new RuntimeException("批量标记通知为已读失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean markAllAsRead(Long userId, String type) {
        log.info("标记所有通知为已读: userId={}, type={}", userId, type);
        try {
            int result = notificationMapper.markAllAsRead(userId, type);
            
            // 优化：重置缓存，全部标记已读后未读数为0
            if (result >= 0) {
                if (type == null || type.isEmpty()) {
                    // 全部标记为已读
                    notificationCacheService.resetUnreadCount(userId);
                } else {
                    // 指定类型标记为已读，需要刷新未读数
                    notificationCacheService.refreshUnreadCount(userId);
                }
                // 清除统计信息缓存
                notificationCacheService.clearUserCache(userId);
                // 清除聚合通知缓存
                clearGroupedNotificationCache(userId);
            }
            
            return result >= 0;
        } catch (Exception e) {
            log.error("标记所有通知为已读失败: userId={}", userId, e);
            throw new RuntimeException("标记所有通知为已读失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteNotification(Long notificationId, Long userId) {
        log.info("删除通知: notificationId={}, userId={}", notificationId, userId);
        try {
            int result = notificationMapper.deleteNotification(notificationId, userId);
            return result > 0;
        } catch (Exception e) {
            log.error("删除通知失败: notificationId={}", notificationId, e);
            throw new RuntimeException("删除通知失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchDeleteNotifications(List<Long> notificationIds, Long userId) {
        log.info("批量删除通知: notificationIds={}, userId={}", notificationIds, userId);
        try {
            if (notificationIds == null || notificationIds.isEmpty()) {
                return false;
            }
            int result = notificationMapper.batchDeleteNotifications(notificationIds, userId);
            return result > 0;
        } catch (Exception e) {
            log.error("批量删除通知失败: userId={}", userId, e);
            throw new RuntimeException("批量删除通知失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean clearReadNotifications(Long userId) {
        log.info("清空已读通知: userId={}", userId);
        try {
            int result = notificationMapper.clearReadNotifications(userId);
            return result >= 0;
        } catch (Exception e) {
            log.error("清空已读通知失败: userId={}", userId, e);
            throw new RuntimeException("清空已读通知失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean createNotification(NotificationCreateRequest request) {
        log.info("创建通知: request={}", request);
        try {
            // 检查用户是否开启了该类型通知
            if (!shouldSendNotification(request.getUserId(), request.getType())) {
                log.info("用户已关闭该类型通知，跳过发送: userId={}, type={}",
                    request.getUserId(), request.getType());
                return false;
            }

            Notification notification = new Notification();
            BeanUtils.copyProperties(request, notification);
            notification.setIsRead(Notification.READ_STATUS_UNREAD);
            notification.setStatus(Notification.STATUS_NORMAL);
            notification.setCreateTime(LocalDateTime.now());

            int result = notificationMapper.insert(notification);

            if (result > 0) {
                Long notificationId = notification.getId();
                Long userId = request.getUserId();
                
                // 优化：增加缓存未读数
                notificationCacheService.incrementUnreadCount(userId, 1);
                // 清除聚合通知缓存
                clearGroupedNotificationCache(userId);
                
                // 通知创建成功后，异步推送WebSocket消息
                // 将推送逻辑移到事务外执行，避免阻塞事务提交
                // 使用异步方式推送，不阻塞当前线程
                // 由于WebSocketNotificationService的方法已经使用@Async，这里直接调用即可
                try {
                    // 检查用户是否开启了WebSocket推送
                    if (shouldPushWebSocket(userId)) {
                        // 查询完整的通知详情（包含发送者、评论、文章等信息）
                        NotificationResponse notificationResponse = notificationMapper.selectNotificationDetail(
                            notificationId, userId
                        );

                        if (notificationResponse != null) {
                            // 异步推送通知给用户（@Async方法会在独立线程池中执行）
                            webSocketNotificationService.pushNotificationToUser(
                                userId, notificationResponse
                            );

                            // 优化：使用缓存服务获取未读数量，减少数据库查询
                            Integer unreadCount = notificationCacheService.getUnreadCount(userId);
                            webSocketNotificationService.pushUnreadCount(userId, unreadCount);

                            log.info("WebSocket推送任务已提交: userId={}, notificationId={}", userId, notificationId);
                        }
                    } else {
                        log.debug("用户已关闭WebSocket推送: userId={}", userId);
                    }
                } catch (Exception e) {
                    // WebSocket推送失败不影响通知创建
                    log.warn("WebSocket推送通知失败: userId={}, notificationId={}", userId, notificationId, e);
                }
            }

            return result > 0;
        } catch (Exception e) {
            log.error("创建通知失败: request={}", request, e);
            throw new RuntimeException("创建通知失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean sendFollowNotification(Long followerId, Long followingId) {
        log.info("发送新增粉丝通知: followerId={}, followingId={}", followerId, followingId);
        try {
            // 不给自己发通知
            if (followerId.equals(followingId)) {
                return false;
            }
            
            // 检查是否已存在相同通知（5分钟内）
            Boolean exists = notificationMapper.existsSimilarNotification(
                followingId, followerId, Notification.TYPE_FOLLOW, 
                Notification.TARGET_TYPE_USER, followerId, 5
            );
            if (exists != null && exists) {
                log.info("5分钟内已存在相同的关注通知，跳过发送");
                return false;
            }
            
            NotificationCreateRequest request = new NotificationCreateRequest();
            request.setUserId(followingId);
            request.setSenderId(followerId);
            request.setType(Notification.TYPE_FOLLOW);
            request.setTitle("新增粉丝");
            request.setContent("关注了你");
            request.setTargetType(Notification.TARGET_TYPE_USER);
            request.setTargetId(followerId);
            
            return createNotification(request);
        } catch (Exception e) {
            log.error("发送新增粉丝通知失败: followerId={}, followingId={}", followerId, followingId, e);
            return false;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean sendLikeNotification(Long userId, Long targetId, String targetType, Long authorId) {
        log.info("发送点赞通知（方案A - 独立通知）: userId={}, targetId={}, targetType={}, authorId={}",
            userId, targetId, targetType, authorId);
        try {
            // 不给自己发通知
            if (userId.equals(authorId)) {
                log.debug("不给自己发送点赞通知");
                return false;
            }

            // 检查用户是否开启了点赞通知
            if (!shouldSendNotification(authorId, Notification.TYPE_LIKE)) {
                log.debug("用户已关闭点赞通知或处于免打扰时段: userId={}", authorId);
                return false;
            }

            // ========== 方案A：每个点赞创建一条独立通知 ==========
            // 检查是否重复点赞（防止短时间内重复通知）
            boolean exists = notificationMapper.existsSimilarNotification(
                authorId, userId, Notification.TYPE_LIKE, targetType, targetId, 5
            );

            if (exists) {
                log.debug("5分钟内已存在相同的点赞通知，跳过: senderId={}", userId);
                return true;
            }

            // 直接创建新通知
            return createSimpleLikeNotification(userId, targetId, targetType, authorId);

        } catch (Exception e) {
            log.error("发送点赞通知失败: userId={}, targetId={}, targetType={}",
                userId, targetId, targetType, e);
            return false;
        }
    }

    /**
     * 创建简单的点赞通知（方案A）
     */
    private boolean createSimpleLikeNotification(Long userId, Long targetId,
                                                 String targetType, Long authorId) {
        try {
            String title;
            String content;
            Map<String, Object> extraData = new HashMap<>();

            // 根据目标类型设置标题和内容
            if ("article".equals(targetType)) {
                title = "文章点赞";
                content = "赞了你的文章";
                extraData.put("targetType", "article");
                extraData.put("targetId", targetId);
            } else if ("comment".equals(targetType)) {
                title = "评论点赞";
                content = "赞了你的评论";
                extraData.put("targetType", "comment");
                extraData.put("targetId", targetId);
            } else {
                title = "点赞";
                content = "赞了你的内容";
            }

            // 创建通知对象
            Notification notification = new Notification();
            notification.setUserId(authorId);
            notification.setSenderId(userId);
            notification.setType(Notification.TYPE_LIKE);
            notification.setTitle(title);
            notification.setContent(content);
            notification.setTargetType(targetType);
            notification.setTargetId(targetId);
            notification.setIsRead(0);
            notification.setStatus(Notification.STATUS_NORMAL);

            // 设置额外数据
            if (!extraData.isEmpty()) {
                try {
                    notification.setExtraData(objectMapper.writeValueAsString(extraData));
                } catch (Exception e) {
                    log.warn("序列化extraData失败", e);
                }
            }

            // 保存到数据库
            int rows = notificationMapper.insert(notification);
            if (rows > 0) {
                log.info("创建点赞通知成功: notificationId={}", notification.getId());

                // 更新统计（通过重新计算）
                try {
                    notificationStatsMapper.recalculateUserStats(authorId);
                } catch (Exception e) {
                    log.warn("更新通知统计失败: userId={}", authorId, e);
                }

                // 推送WebSocket通知
                if (shouldPushWebSocket(authorId)) {
                    try {
                        // 查询完整的通知详情
                        NotificationResponse notificationResponse = notificationMapper.selectNotificationDetail(
                            notification.getId(), authorId
                        );
                        if (notificationResponse != null) {
                            webSocketNotificationService.pushNotificationToUser(authorId, notificationResponse);

                            // 推送未读数量
                            Integer unreadCount = notificationMapper.countUnreadNotifications(authorId);
                            webSocketNotificationService.pushUnreadCount(authorId, unreadCount);
                        }
                    } catch (Exception e) {
                        log.warn("推送WebSocket通知失败: userId={}", authorId, e);
                    }
                }

                return true;
            }
            return false;

        } catch (Exception e) {
            log.error("创建点赞通知失败", e);
            return false;
        }
    }

    /**
     * 创建新的聚合点赞通知（已废弃 - 方案B）
     * @deprecated 已切换到方案A，每个点赞创建独立通知
     */
    @Deprecated
    private boolean createAggregatedLikeNotification(Long userId, Long targetId,
                                                     String targetType, Long authorId) {
        try {
            String title;
            String content;
            Map<String, Object> extraData = new HashMap<>();

            if (Notification.TARGET_TYPE_ARTICLE.equals(targetType)) {
                // 点赞文章
                title = "文章点赞";
                content = "赞了你的文章";
                Article article = articleMapper.selectById(targetId);
                if (article != null) {
                    extraData.put("articleId", article.getId());
                    extraData.put("articleTitle", article.getTitle());
                    extraData.put("articleSummary", article.getSummary());
                }
            } else if (Notification.TARGET_TYPE_COMMENT.equals(targetType)) {
                // 点赞评论
                title = "评论点赞";
                content = "赞了你的评论";
                Comment comment = commentMapper.selectById(targetId);
                if (comment != null) {
                    extraData.put("commentId", comment.getId());
                    extraData.put("commentContent", comment.getContent());
                    extraData.put("articleId", comment.getArticleId());

                    // 查询文章信息
                    Article article = articleMapper.selectById(comment.getArticleId());
                    if (article != null) {
                        extraData.put("articleTitle", article.getTitle());
                        extraData.put("articleSummary", article.getSummary());
                    }
                }
            } else {
                log.warn("未知的目标类型: {}", targetType);
                return false;
            }

            // 创建通知对象
            Notification notification = new Notification();
            notification.setUserId(authorId);
            notification.setSenderId(userId);
            notification.setType(Notification.TYPE_LIKE);
            notification.setTitle(title);
            notification.setContent(content);
            notification.setTargetType(targetType);
            notification.setTargetId(targetId);
            notification.setIsRead(0);
            notification.setStatus(Notification.STATUS_NORMAL);

            // 设置聚合字段
            notification.setSenderCount(1);  // 初始为1人
            notification.setSenderIds("[" + userId + "]");  // JSON数组
            notification.setLastSenderId(userId);
            notification.setLastUpdateTime(LocalDateTime.now());

            // 设置额外数据
            if (!extraData.isEmpty()) {
                try {
                    notification.setExtraData(objectMapper.writeValueAsString(extraData));
                } catch (Exception e) {
                    log.warn("序列化extraData失败", e);
                }
            }

            // 保存到数据库
            int rows = notificationMapper.insert(notification);
            if (rows > 0) {
                log.info("创建聚合点赞通知成功: notificationId={}", notification.getId());

                // 更新统计（通过重新计算）
                try {
                    notificationStatsMapper.recalculateUserStats(authorId);
                } catch (Exception e) {
                    log.warn("更新通知统计失败: userId={}", authorId, e);
                }

                // 推送WebSocket通知
                if (shouldPushWebSocket(authorId)) {
                    try {
                        // 查询完整的通知详情
                        NotificationResponse notificationResponse = notificationMapper.selectNotificationDetail(
                            notification.getId(), authorId
                        );
                        if (notificationResponse != null) {
                            webSocketNotificationService.pushNotificationToUser(authorId, notificationResponse);

                            // 推送未读数量
                            Integer unreadCount = notificationMapper.countUnreadNotifications(authorId);
                            webSocketNotificationService.pushUnreadCount(authorId, unreadCount);
                        }
                    } catch (Exception e) {
                        log.warn("推送WebSocket通知失败: userId={}", authorId, e);
                    }
                }

                return true;
            }
            return false;

        } catch (Exception e) {
            log.error("创建聚合点赞通知失败", e);
            return false;
        }
    }

    /**
     * 更新已存在的聚合点赞通知（已废弃 - 方案B）
     * @deprecated 已切换到方案A，每个点赞创建独立通知
     */
    @Deprecated
    private boolean updateAggregatedLikeNotification(Notification existingNotification, Long newSenderId) {
        try {
            // 解析现有的发送者ID列表
            List<Long> senderIdList = parseSenderIds(existingNotification.getSenderIds());

            // 检查是否已经包含该发送者（防止重复）
            if (senderIdList.contains(newSenderId)) {
                log.info("该用户已经点赞过，不重复添加: senderId={}", newSenderId);
                return true;
            }

            // 添加新的发送者ID（最多保存100个）
            if (senderIdList.size() < 100) {
                senderIdList.add(newSenderId);
            }

            // 转换为JSON字符串
            String updatedSenderIds = senderIdList.toString();  // [1, 2, 3]

            // 更新数据库
            int rows = notificationMapper.updateAggregatedLikeNotification(
                existingNotification.getId(),
                newSenderId,
                updatedSenderIds,
                existingNotification.getSenderCount() + 1
            );

            if (rows > 0) {
                log.info("更新聚合点赞通知成功: notificationId={}, 新增点赞者={}, 总人数={}",
                    existingNotification.getId(), newSenderId, existingNotification.getSenderCount() + 1);

                // 更新统计（通过重新计算）
                try {
                    notificationStatsMapper.recalculateUserStats(existingNotification.getUserId());
                } catch (Exception e) {
                    log.warn("更新通知统计失败: userId={}", existingNotification.getUserId(), e);
                }

                // 推送WebSocket通知
                if (shouldPushWebSocket(existingNotification.getUserId())) {
                    try {
                        // 重新查询完整的通知信息
                        NotificationResponse notificationResponse = notificationMapper.selectNotificationDetail(
                            existingNotification.getId(), existingNotification.getUserId()
                        );
                        if (notificationResponse != null) {
                            webSocketNotificationService.pushNotificationToUser(
                                existingNotification.getUserId(), notificationResponse
                            );

                            // 推送未读数量
                            Integer unreadCount = notificationMapper.countUnreadNotifications(
                                existingNotification.getUserId()
                            );
                            webSocketNotificationService.pushUnreadCount(
                                existingNotification.getUserId(), unreadCount
                            );
                        }
                    } catch (Exception e) {
                        log.warn("推送WebSocket通知失败: userId={}", existingNotification.getUserId(), e);
                    }
                }

                return true;
            }
            return false;

        } catch (Exception e) {
            log.error("更新聚合点赞通知失败: notificationId={}", existingNotification.getId(), e);
            return false;
        }
    }

    /**
     * 解析发送者ID列表（JSON字符串转List）
     */
    private List<Long> parseSenderIds(String senderIdsJson) {
        List<Long> result = new ArrayList<>();
        if (senderIdsJson == null || senderIdsJson.trim().isEmpty()) {
            return result;
        }

        try {
            // 去掉方括号，按逗号分割
            String cleaned = senderIdsJson.replace("[", "").replace("]", "").trim();
            if (cleaned.isEmpty()) {
                return result;
            }

            String[] ids = cleaned.split(",");
            for (String id : ids) {
                try {
                    result.add(Long.parseLong(id.trim()));
                } catch (NumberFormatException e) {
                    log.warn("解析发送者ID失败: {}", id);
                }
            }
        } catch (Exception e) {
            log.error("解析发送者ID列表失败: {}", senderIdsJson, e);
        }

        return result;
    }

    /**
     * 发送收藏通知 (已废弃,不再发送收藏通知)
     * @deprecated 收藏通知已废弃,不再发送
     */
    @Override
    @Deprecated
    @Transactional(rollbackFor = Exception.class)
    public boolean sendCollectNotification(Long userId, Long articleId, Long authorId) {
        log.info("收藏通知已废弃,不再发送: userId={}, articleId={}, authorId={}", userId, articleId, authorId);
        // 收藏通知已废弃,直接返回true表示操作成功(但不发送通知)
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean sendCommentNotification(Long commentId, Long userId, Long articleId, Long authorId) {
        log.info("发送评论通知: commentId={}, userId={}, articleId={}, authorId={}",
            commentId, userId, articleId, authorId);
        try {
            // 不给自己发通知
            if (userId.equals(authorId)) {
                return false;
            }

            // 检查是否已存在相同通知（1分钟内）
            Boolean exists = notificationMapper.existsSimilarNotification(
                authorId, userId, Notification.TYPE_COMMENT,
                Notification.TARGET_TYPE_COMMENT, commentId, 1
            );
            if (exists != null && exists) {
                log.info("1分钟内已存在相同的评论通知，跳过发送");
                return false;
            }

            String content = "评论了你的文章";
            Map<String, Object> extraData = new HashMap<>();

            // 查询评论内容
            Comment comment = commentMapper.selectById(commentId);
            if (comment != null) {
                extraData.put("commentId", comment.getId());
                extraData.put("commentContent", comment.getContent());
                extraData.put("articleId", articleId);
            }

            // 查询文章信息
            Article article = articleMapper.selectById(articleId);
            if (article != null) {
                extraData.put("articleTitle", article.getTitle());
                extraData.put("articleSummary", article.getSummary());
            }

            NotificationCreateRequest request = new NotificationCreateRequest();
            request.setUserId(authorId);
            request.setSenderId(userId);
            request.setType(Notification.TYPE_COMMENT);
            request.setTitle("文章评论");
            request.setContent(content);
            // COMMENT类型指向评论，target显示评论内容，文章信息通过extraData传递
            request.setTargetType(Notification.TARGET_TYPE_COMMENT);
            request.setTargetId(commentId);

            // 设置额外数据
            if (!extraData.isEmpty()) {
                try {
                    request.setExtraData(objectMapper.writeValueAsString(extraData));
                } catch (Exception e) {
                    log.warn("序列化extraData失败", e);
                }
            }

            return createNotification(request);
        } catch (Exception e) {
            log.error("发送评论通知失败: commentId={}, userId={}", commentId, userId, e);
            return false;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean sendReplyNotification(Long commentId, Long userId, Long replyToUserId, Long articleId) {
        log.info("发送回复通知: commentId={}, userId={}, replyToUserId={}, articleId={}",
            commentId, userId, replyToUserId, articleId);
        try {
            // 不给自己发通知
            if (userId.equals(replyToUserId)) {
                return false;
            }

            // 检查是否已存在相同通知（1分钟内）
            Boolean exists = notificationMapper.existsSimilarNotification(
                replyToUserId, userId, Notification.TYPE_REPLY,
                Notification.TARGET_TYPE_COMMENT, commentId, 1
            );
            if (exists != null && exists) {
                log.info("1分钟内已存在相同的回复通知，跳过发送");
                return false;
            }

            String content = "回复了你的评论";
            Map<String, Object> extraData = new HashMap<>();

            // 查询回复的评论内容（新评论）
            Comment replyComment = commentMapper.selectById(commentId);
            if (replyComment != null) {
                extraData.put("replyCommentId", replyComment.getId());
                extraData.put("replyContent", replyComment.getContent());
                extraData.put("articleId", articleId);

                // 查询被回复的评论内容（原评论）
                Comment originalComment = null;
                if (replyComment.getParentId() != null && replyComment.getParentId() > 0) {
                    originalComment = commentMapper.selectById(replyComment.getParentId());
                }

                if (originalComment != null) {
                    extraData.put("originalCommentId", originalComment.getId());
                    extraData.put("originalCommentContent", originalComment.getContent());
                }
            }

            // 查询文章信息
            Article article = articleMapper.selectById(articleId);
            if (article != null) {
                extraData.put("articleTitle", article.getTitle());
                extraData.put("articleSummary", article.getSummary());
            }

            NotificationCreateRequest request = new NotificationCreateRequest();
            request.setUserId(replyToUserId);
            request.setSenderId(userId);
            request.setType(Notification.TYPE_REPLY);
            request.setTitle("评论回复");
            request.setContent(content);
            request.setTargetType(Notification.TARGET_TYPE_COMMENT);
            request.setTargetId(commentId);

            // 设置额外数据
            if (!extraData.isEmpty()) {
                try {
                    request.setExtraData(objectMapper.writeValueAsString(extraData));
                } catch (Exception e) {
                    log.warn("序列化extraData失败", e);
                }
            }

            return createNotification(request);
        } catch (Exception e) {
            log.error("发送回复通知失败: commentId={}, userId={}", commentId, userId, e);
            return false;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean sendMentionNotification(Long commentId, Long userId, List<Long> mentionedUserIds, Long articleId) {
        log.info("发送@提及通知: commentId={}, userId={}, mentionedUserIds={}, articleId={}",
            commentId, userId, mentionedUserIds, articleId);
        try {
            if (mentionedUserIds == null || mentionedUserIds.isEmpty()) {
                log.info("没有被@提及的用户，跳过发送");
                return false;
            }

            // 查询评论内容
            Comment comment = commentMapper.selectById(commentId);
            if (comment == null) {
                log.warn("评论不存在: commentId={}", commentId);
                return false;
            }

            // 查询文章信息
            Article article = articleMapper.selectById(articleId);
            if (article == null) {
                log.warn("文章不存在: articleId={}", articleId);
                return false;
            }

            // 为每个被@的用户发送通知
            int successCount = 0;
            for (Long mentionedUserId : mentionedUserIds) {
                // 不给自己发通知
                if (userId.equals(mentionedUserId)) {
                    continue;
                }

                // 检查是否已存在相同通知（1分钟内）
                Boolean exists = notificationMapper.existsSimilarNotification(
                    mentionedUserId, userId, Notification.TYPE_MENTION,
                    Notification.TARGET_TYPE_COMMENT, commentId, 1
                );
                if (exists != null && exists) {
                    log.info("1分钟内已存在相同的@提及通知，跳过发送: mentionedUserId={}", mentionedUserId);
                    continue;
                }

                String content = "在评论中提到了你";
                Map<String, Object> extraData = new HashMap<>();
                extraData.put("commentId", commentId);
                extraData.put("commentContent", comment.getContent());
                extraData.put("articleId", articleId);
                extraData.put("articleTitle", article.getTitle());
                extraData.put("articleSummary", article.getSummary());

                NotificationCreateRequest request = new NotificationCreateRequest();
                request.setUserId(mentionedUserId);
                request.setSenderId(userId);
                request.setType(Notification.TYPE_MENTION);
                request.setTitle("@提及");
                request.setContent(content);
                request.setTargetType(Notification.TARGET_TYPE_COMMENT);
                request.setTargetId(commentId);

                // 设置额外数据
                if (!extraData.isEmpty()) {
                    try {
                        request.setExtraData(objectMapper.writeValueAsString(extraData));
                    } catch (Exception e) {
                        log.warn("序列化extraData失败", e);
                    }
                }

                if (createNotification(request)) {
                    successCount++;
                }
            }

            log.info("@提及通知发送完成: 成功={}, 总数={}", successCount, mentionedUserIds.size());
            return successCount > 0;
        } catch (Exception e) {
            log.error("发送@提及通知失败: commentId={}, userId={}", commentId, userId, e);
            return false;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean sendSystemNotification(Long userId, String title, String content) {
        log.info("发送系统通知: userId={}, title={}", userId, title);
        try {
            NotificationCreateRequest request = new NotificationCreateRequest();
            request.setUserId(userId);
            request.setSenderId(null);
            request.setType(Notification.TYPE_SYSTEM);
            request.setTitle(title);
            request.setContent(content);
            request.setTargetType(null);
            request.setTargetId(null);

            return createNotification(request);
        } catch (Exception e) {
            log.error("发送系统通知失败: userId={}, title={}", userId, title, e);
            return false;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchSendSystemNotification(List<Long> userIds, String title, String content) {
        log.info("批量发送系统通知: userIds={}, title={}", userIds, title);
        try {
            if (userIds == null || userIds.isEmpty()) {
                return false;
            }

            int successCount = 0;
            for (Long userId : userIds) {
                if (sendSystemNotification(userId, title, content)) {
                    successCount++;
                }
            }

            log.info("批量发送系统通知完成: 总数={}, 成功={}", userIds.size(), successCount);
            return successCount > 0;
        } catch (Exception e) {
            log.error("批量发送系统通知失败: userIds={}", userIds, e);
            return false;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean initUserNotificationStats(Long userId) {
        log.info("初始化用户通知统计: userId={}", userId);
        try {
            int result = notificationStatsMapper.initUserStats(userId);
            return result > 0;
        } catch (Exception e) {
            log.error("初始化用户通知统计失败: userId={}", userId, e);
            return false;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean recalculateUserNotificationStats(Long userId) {
        log.info("重新计算用户通知统计: userId={}", userId);
        try {
            int result = notificationStatsMapper.recalculateUserStats(userId);
            return result > 0;
        } catch (Exception e) {
            log.error("重新计算用户通知统计失败: userId={}", userId, e);
            return false;
        }
    }

    /**
     * 检查是否应该发送通知
     * 综合考虑用户设置和免打扰时段
     */
    private boolean shouldSendNotification(Long userId, String notificationType) {
        try {
            // 检查用户是否开启了该类型通知
            if (!notificationSettingsService.isNotificationEnabled(userId, notificationType)) {
                log.debug("用户已关闭该类型通知: userId={}, type={}", userId, notificationType);
                return false;
            }

            // 检查是否处于免打扰时段
            if (notificationSettingsService.isInDoNotDisturbPeriod(userId)) {
                log.debug("用户处于免打扰时段: userId={}", userId);
                return false;
            }

            return true;
        } catch (Exception e) {
            log.error("检查通知设置失败: userId={}, type={}", userId, notificationType, e);
            // 出错时默认发送
            return true;
        }
    }

    /**
     * 检查是否应该推送WebSocket通知
     */
    private boolean shouldPushWebSocket(Long userId) {
        try {
            return notificationSettingsService.isWebSocketEnabled(userId);
        } catch (Exception e) {
            log.error("检查WebSocket设置失败: userId={}", userId, e);
            // 出错时默认推送
            return true;
        }
    }

    /**
     * 清除聚合通知缓存
     * 在通知变更时调用（创建、已读、删除等）
     */
    private void clearGroupedNotificationCache(Long userId) {
        if (userId == null) {
            return;
        }
        
        String cacheKey = "notification:grouped:" + userId;
        try {
            redisTemplate.delete(cacheKey);
            log.debug("清除聚合通知缓存: userId={}", userId);
        } catch (Exception e) {
            log.warn("清除聚合通知缓存失败: userId={}", userId, e);
        }
    }

    @Override
    public IPage<NotificationGroupResponse> getGroupedNotificationPage(Long userId, NotificationQueryRequest query) {
        log.info("分页查询聚合通知列表: userId={}, query={}", userId, query);
        try {
            // 优化：只缓存首页，无筛选条件的默认查询
            boolean isFirstPageDefault = query.getCurrent() == 1 
                && query.getSize() == 20 
                && query.getType() == null 
                && query.getIsRead() == null
                && query.getOnlyUnread() == null;
            
            if (isFirstPageDefault) {
                // 尝试从缓存获取
                String cacheKey = "notification:grouped:" + userId;
                try {
                    @SuppressWarnings("unchecked")
                    IPage<NotificationGroupResponse> cached = 
                        (IPage<NotificationGroupResponse>) redisTemplate.opsForValue().get(cacheKey);
                    
                    if (cached != null && cached.getRecords() != null) {
                        log.debug("从缓存获取聚合通知列表: userId={}", userId);
                        return cached;
                    }
                } catch (Exception e) {
                    log.warn("获取聚合通知缓存失败: userId={}", userId, e);
                }
                
                // 缓存未命中，查询数据库
                Page<NotificationGroupResponse> page = new Page<>(query.getCurrent(), query.getSize());
                IPage<NotificationGroupResponse> result = notificationMapper.selectGroupedNotificationPage(page, userId, query);
                batchFillGroupedNotificationData(result.getRecords(), userId);
                
                // 缓存结果（5分钟）
                try {
                    redisTemplate.opsForValue().set(cacheKey, result, 300, TimeUnit.SECONDS);
                    log.debug("缓存聚合通知列表: userId={}", userId);
                } catch (Exception e) {
                    log.warn("缓存聚合通知列表失败: userId={}", userId, e);
                }
                
                return result;
            }
            
            // 非首页或有筛选条件，直接查询数据库
            Page<NotificationGroupResponse> page = new Page<>(query.getCurrent(), query.getSize());
            IPage<NotificationGroupResponse> result = notificationMapper.selectGroupedNotificationPage(page, userId, query);
            batchFillGroupedNotificationData(result.getRecords(), userId);
            
            return result;
        } catch (Exception e) {
            log.error("分页查询聚合通知列表失败: userId={}", userId, e);
            throw new RuntimeException("查询聚合通知列表失败", e);
        }
    }

    @Override
    public List<NotificationResponse> getGroupedNotificationDetails(Long userId, String type, String targetType, Long targetId) {
        log.info("获取分组通知详情: userId={}, type={}, targetType={}, targetId={}", userId, type, targetType, targetId);
        try {
            // 使用优化后的批量查询方法，一次性获取所有通知详情
            List<NotificationResponse> details = notificationMapper.selectGroupDetailNotifications(
                userId, type, targetType, targetId
            );

            if (details.isEmpty()) {
                return details;
            }

            // 填充目标信息（文章/评论）
            fillGroupDetailsTargetInfo(details, userId, targetType, targetId);

            // 设置操作文本
            for (NotificationResponse detail : details) {
                detail.setActionText(getActionText(detail.getType(), detail.getTargetType()));
                detail.setTypeName(getTypeName(detail.getType()));
            }

            return details;
        } catch (Exception e) {
            log.error("获取分组通知详情失败: userId={}, type={}, targetType={}, targetId={}",
                userId, type, targetType, targetId, e);
            throw new RuntimeException("获取分组通知详情失败", e);
        }
    }

    /**
     * 填充分组详情的目标信息
     */
    private void fillGroupDetailsTargetInfo(List<NotificationResponse> details, Long userId, String targetType, Long targetId) {
        if (details == null || details.isEmpty() || targetId == null) {
            return;
        }

        try {
            if ("article".equals(targetType)) {
                // 查询文章信息
                List<Map<String, Object>> articles = notificationMapper.selectArticlesByIds(
                    java.util.Collections.singletonList(targetId)
                );
                if (!articles.isEmpty()) {
                    Map<String, Object> article = articles.get(0);
                    NotificationResponse.TargetInfo target = new NotificationResponse.TargetInfo();
                    target.setId(targetId);
                    target.setType("article");
                    target.setTitle((String) article.get("title"));
                    target.setSummary((String) article.get("summary"));
                    target.setUrl("/article/" + targetId);

                    // 为所有详情设置相同的目标信息
                    for (NotificationResponse detail : details) {
                        detail.setTarget(target);
                    }
                }
            } else if ("comment".equals(targetType)) {
                // 查询评论信息
                List<Map<String, Object>> comments = notificationMapper.selectCommentsByIds(
                    java.util.Collections.singletonList(targetId), userId
                );
                if (!comments.isEmpty()) {
                    Map<String, Object> comment = comments.get(0);
                    NotificationResponse.TargetInfo target = new NotificationResponse.TargetInfo();
                    target.setId(targetId);
                    target.setType("comment");
                    target.setTitle((String) comment.get("content"));

                    Long articleId = comment.get("articleId") != null ?
                        ((Number) comment.get("articleId")).longValue() : null;
                    if (articleId != null) {
                        target.setUrl("/article/" + articleId + "#comment-" + targetId);
                    }

                    // 为所有详情设置相同的目标信息
                    for (NotificationResponse detail : details) {
                        detail.setTarget(target);
                    }
                }
            }
        } catch (Exception e) {
            log.warn("填充分组详情目标信息失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean markGroupAsRead(Long userId, String type, String targetType, Long targetId) {
        log.info("标记分组通知为已读: userId={}, type={}, targetType={}, targetId={}", userId, type, targetType, targetId);
        try {
            // 查询该分组的所有通知ID
            List<Long> notificationIds = notificationMapper.selectNotificationIdsByGroup(userId, type, targetType, targetId);

            if (notificationIds.isEmpty()) {
                return true;
            }

            // 批量标记为已读
            int result = notificationMapper.batchMarkAsRead(notificationIds, userId);

            if (result > 0) {
                // 更新缓存
                notificationCacheService.decrementUnreadCount(userId, result);
                notificationCacheService.clearUserCache(userId);
                clearGroupedNotificationCache(userId);
                
                // 推送未读数量更新
                try {
                    if (shouldPushWebSocket(userId)) {
                        Integer unreadCount = notificationCacheService.getUnreadCount(userId);
                        webSocketNotificationService.pushUnreadCount(userId, unreadCount);
                    }
                } catch (Exception e) {
                    log.warn("推送未读数量失败: userId={}", userId, e);
                }
            }

            return result > 0;
        } catch (Exception e) {
            log.error("标记分组通知为已读失败: userId={}, type={}, targetType={}, targetId={}",
                userId, type, targetType, targetId, e);
            throw new RuntimeException("标记分组通知为已读失败", e);
        }
    }

    /**
     * 批量填充聚合通知的关联数据
     * 优化性能：避免N+1查询问题
     */
    private void batchFillGroupedNotificationData(List<NotificationGroupResponse> groups, Long userId) {
        if (groups == null || groups.isEmpty()) {
            return;
        }

        try {
            // 1. 收集需要查询的ID和groupKeys
            List<Long> articleIds = new ArrayList<>();
            List<Long> commentIds = new ArrayList<>();
            List<String> groupKeys = new ArrayList<>();

            for (NotificationGroupResponse group : groups) {
                groupKeys.add(group.getGroupKey());

                String targetType = group.getTargetType();
                Long targetId = group.getTargetId();
                if (targetId != null) {
                    if ("article".equals(targetType)) {
                        articleIds.add(targetId);
                    } else if ("comment".equals(targetType)) {
                        commentIds.add(targetId);
                    }
                }
            }
            
            // 去重
            articleIds = new ArrayList<>(new java.util.HashSet<>(articleIds));
            commentIds = new ArrayList<>(new java.util.HashSet<>(commentIds));

            // 2. 批量查询文章信息（1次查询）
            Map<Long, Map<String, Object>> articleMap = new HashMap<>();
            if (!articleIds.isEmpty()) {
                List<Map<String, Object>> articles = notificationMapper.selectArticlesByIds(articleIds);
                for (Map<String, Object> article : articles) {
                    Long id = ((Number) article.get("id")).longValue();
                    articleMap.put(id, article);
                }
            }

            // 3. 批量查询评论信息（1次查询）
            Map<Long, Map<String, Object>> commentMap = new HashMap<>();
            if (!commentIds.isEmpty()) {
                List<Map<String, Object>> comments = notificationMapper.selectCommentsByIds(commentIds, userId);
                
                // 存储评论，并收集父评论ID
                List<Long> parentCommentIds = new ArrayList<>();
                for (Map<String, Object> comment : comments) {
                    Long id = ((Number) comment.get("id")).longValue();
                    commentMap.put(id, comment);
                    
                    // 收集父评论ID（用于REPLY类型）
                    Object parentIdObj = comment.get("parentId");
                    if (parentIdObj != null) {
                        Long parentId = ((Number) parentIdObj).longValue();
                        if (parentId > 0 && !commentMap.containsKey(parentId)) {
                            parentCommentIds.add(parentId);
                        }
                    }
                }
                
                // 如果有父评论需要查询，再查一次
                if (!parentCommentIds.isEmpty()) {
                    List<Long> uniqueParentIds = new ArrayList<>(new java.util.HashSet<>(parentCommentIds));
                    List<Map<String, Object>> parentComments = 
                        notificationMapper.selectCommentsByIds(uniqueParentIds, userId);
                    for (Map<String, Object> parentComment : parentComments) {
                        Long id = ((Number) parentComment.get("id")).longValue();
                        commentMap.put(id, parentComment);
                    }
                }
            }

            // 4. 批量查询所有分组的发送者（1次查询，替代N次查询）
            Map<String, List<NotificationGroupResponse.SenderInfo>> sendersMap = new HashMap<>();
            if (!groupKeys.isEmpty()) {
                List<Map<String, Object>> allSenders = notificationMapper.selectSendersByGroups(userId, groupKeys);
                log.debug("批量查询发送者: userId={}, groupKeys={}, 结果数={}", userId, groupKeys.size(), allSenders.size());

                // 按groupKey分组，并限制每组最多3个
                for (Map<String, Object> senderMap : allSenders) {
                    String groupKey = (String) senderMap.get("groupKey");
                    
                    // 获取该组现有的发送者列表
                    List<NotificationGroupResponse.SenderInfo> groupSenders = 
                        sendersMap.computeIfAbsent(groupKey, k -> new ArrayList<>());
                    
                    // 只保留前3个
                    if (groupSenders.size() < 3) {
                        NotificationGroupResponse.SenderInfo sender = new NotificationGroupResponse.SenderInfo();
                        sender.setUserId(((Number) senderMap.get("userId")).longValue());
                        sender.setNickName((String) senderMap.get("nickName"));
                        sender.setAvatar((String) senderMap.get("avatar"));

                        Object latestTimeObj = senderMap.get("latestTime");
                        if (latestTimeObj != null) {
                            sender.setLatestTime(latestTimeObj.toString());
                        }

                        groupSenders.add(sender);
                    }
                }
            }

            // 5. 填充每个分组的数据
            for (NotificationGroupResponse group : groups) {
                // 设置通知类型名称
                group.setTypeName(getTypeName(group.getType()));
                
                // 填充目标信息
                fillGroupTargetInfo(group, articleMap, commentMap);
                
                log.debug("填充分组数据: groupKey={}, targetType={}, targetId={}, target={}", 
                    group.getGroupKey(), group.getTargetType(), group.getTargetId(), group.getTarget());

                // 从Map中获取发送者，无需查询数据库
                List<NotificationGroupResponse.SenderInfo> senders =
                    sendersMap.getOrDefault(group.getGroupKey(), new ArrayList<>());
                group.setSenders(senders);
                
                log.debug("发送者信息: groupKey={}, sendersCount={}", group.getGroupKey(), senders.size());

                // 计算其他发送者数量
                int actualSenderCount = group.getCount() != null ? group.getCount() : senders.size();
                if (actualSenderCount > senders.size()) {
                    group.setOtherSendersCount(actualSenderCount - senders.size());
                } else {
                    group.setOtherSendersCount(0);
                }

                // 通知ID列表已移除（前端通常不需要，如需要可单独接口查询）
                // 这样可以进一步减少查询，提升性能

                // 生成聚合标题
                group.setAggregatedTitle(group.generateAggregatedTitle());
            }

        } catch (Exception e) {
            log.error("批量填充聚合通知数据失败", e);
            // 填充失败不影响主流程，只记录日志
        }
    }

    /**
     * 填充分组的目标信息
     */
    private void fillGroupTargetInfo(NotificationGroupResponse group,
                                     Map<Long, Map<String, Object>> articleMap,
                                     Map<Long, Map<String, Object>> commentMap) {
        String targetType = group.getTargetType();
        Long targetId = group.getTargetId();

        if (targetId == null) {
            return;
        }

        NotificationGroupResponse.TargetInfo target = new NotificationGroupResponse.TargetInfo();
        target.setId(targetId);
        target.setType(targetType);

        if ("article".equals(targetType)) {
            Map<String, Object> article = articleMap.get(targetId);
            if (article != null) {
                target.setTitle((String) article.get("title"));
                target.setContentSummary((String) article.get("summary"));
                target.setUrl("/article/" + targetId);
            }
        } else if ("comment".equals(targetType)) {
            Map<String, Object> comment = commentMap.get(targetId);
            if (comment != null) {
                String content = (String) comment.get("content");
                String articleTitle = (String) comment.get("articleTitle");
                Long parentId = comment.get("parentId") != null ? 
                    ((Number) comment.get("parentId")).longValue() : null;

                // 判断是否为REPLY类型
                if ("REPLY".equals(group.getType())) {
                    // REPLY类型：targetId是回复的评论（新评论）
                    // title: 被回复的评论内容（父评论）
                    // contentSummary: 回复的内容（新评论）
                    
                    // 设置回复内容（新评论）
                    target.setContentSummary(content != null && content.length() > 20 ? 
                        content.substring(0, 20) + "..." : content);
                    
                    // 如果有父评论，查询父评论内容
                    if (parentId != null && parentId > 0) {
                        Map<String, Object> parentComment = commentMap.get(parentId);
                        if (parentComment != null) {
                            String parentContent = (String) parentComment.get("content");
                            // 设置被回复的评论内容（父评论）
                            target.setTitle(parentContent != null && parentContent.length() > 20 ? 
                                parentContent.substring(0, 20) + "..." : parentContent);
                        } else {
                            // 父评论未查询到，使用默认文本
                            target.setTitle("你的评论");
                        }
                    } else {
                        target.setTitle("你的评论");
                    }
                } else {
                    // 其他类型（LIKE/COMMENT）：targetId就是目标评论
                    // title 显示评论内容摘要
                    target.setTitle(content != null && content.length() > 20 ? 
                        content.substring(0, 20) + "..." : content);
                    // contentSummary 显示完整评论内容
                    target.setContentSummary(content != null && content.length() > 50 ? 
                        content.substring(0, 50) + "..." : content);
                }
                
                // 设置URL（包含文章ID和评论ID）
                Object articleIdObj = comment.get("articleId");
                if (articleIdObj != null) {
                    Long articleId = ((Number) articleIdObj).longValue();
                    target.setUrl("/article/" + articleId + "#comment-" + targetId);
                }
                
                // 保存文章标题用于生成聚合标题
                if (articleTitle != null) {
                    group.setArticleTitle(articleTitle);
                }
            }
        }

        group.setTarget(target);
    }

    /**
     * 批量填充通知的关联数据（文章、评论、用户等）
     * 优化性能：避免N+1查询问题
     */
    private void batchFillNotificationData(List<NotificationResponse> notifications, Long userId) {
        if (notifications == null || notifications.isEmpty()) {
            return;
        }

        try {
            // 收集需要查询的ID
            List<Long> articleIds = new ArrayList<>();
            List<Long> commentIds = new ArrayList<>();
            List<Long> userIds = new ArrayList<>();

            for (NotificationResponse notification : notifications) {
                String targetType = notification.getTargetType();
                Long targetId = notification.getTargetId();

                if (targetId != null) {
                    if ("article".equals(targetType)) {
                        articleIds.add(targetId);
                    } else if ("comment".equals(targetType)) {
                        commentIds.add(targetId);
                    } else if ("user".equals(targetType)) {
                        userIds.add(targetId);
                    }
                }
            }

            // 批量查询文章信息
            Map<Long, Map<String, Object>> articleMap = new HashMap<>();
            if (!articleIds.isEmpty()) {
                List<Map<String, Object>> articles = notificationMapper.selectArticlesByIds(articleIds);
                for (Map<String, Object> article : articles) {
                    Long id = ((Number) article.get("id")).longValue();
                    articleMap.put(id, article);
                }
            }

            // 批量查询评论信息
            Map<Long, Map<String, Object>> commentMap = new HashMap<>();
            if (!commentIds.isEmpty()) {
                List<Map<String, Object>> comments = notificationMapper.selectCommentsByIds(commentIds, userId);
                
                // 收集父评论ID（用于REPLY类型）
                List<Long> parentCommentIds = new ArrayList<>();
                for (Map<String, Object> comment : comments) {
                    Long id = ((Number) comment.get("id")).longValue();
                    commentMap.put(id, comment);
                    
                    // 收集父评论ID
                    Object parentIdObj = comment.get("parentId");
                    if (parentIdObj != null) {
                        Long parentId = ((Number) parentIdObj).longValue();
                        if (parentId > 0 && !commentMap.containsKey(parentId)) {
                            parentCommentIds.add(parentId);
                        }
                    }
                }
                
                // 如果有父评论需要查询，批量查询父评论
                if (!parentCommentIds.isEmpty()) {
                    List<Long> uniqueParentIds = new ArrayList<>(new java.util.HashSet<>(parentCommentIds));
                    List<Map<String, Object>> parentComments = 
                        notificationMapper.selectCommentsByIds(uniqueParentIds, userId);
                    for (Map<String, Object> parentComment : parentComments) {
                        Long id = ((Number) parentComment.get("id")).longValue();
                        commentMap.put(id, parentComment);
                        
                        // 同时收集父评论的作者ID
                        Object parentAuthorIdObj = parentComment.get("userId");
                        if (parentAuthorIdObj != null) {
                            Long parentAuthorId = ((Number) parentAuthorIdObj).longValue();
                            if (!userIds.contains(parentAuthorId)) {
                                userIds.add(parentAuthorId);
                            }
                        }
                    }
                }
            }

            // 批量查询用户信息
            Map<Long, Map<String, Object>> userMap = new HashMap<>();
            if (!userIds.isEmpty()) {
                List<Map<String, Object>> users = notificationMapper.selectUsersByIds(userIds);
                for (Map<String, Object> user : users) {
                    Long id = ((Number) user.get("userId")).longValue();
                    userMap.put(id, user);
                }
            }

            // 填充数据到通知对象
            for (NotificationResponse notification : notifications) {
                fillNotificationTargetInfo(notification, articleMap, commentMap, userMap, userId);
            }

        } catch (Exception e) {
            log.error("批量填充通知数据失败", e);
            // 填充失败不影响主流程，只记录日志
        }
    }

    /**
     * 填充单个通知的目标信息
     */
    private void fillNotificationTargetInfo(NotificationResponse notification,
                                           Map<Long, Map<String, Object>> articleMap,
                                           Map<Long, Map<String, Object>> commentMap,
                                           Map<Long, Map<String, Object>> userMap,
                                           Long userId) {
        String targetType = notification.getTargetType();
        Long targetId = notification.getTargetId();

        if (targetId == null) {
            return;
        }

        // 优化: target 对象只保留必要信息 (id/type/url),不再填充冗余的 title/summary
        // 标题和内容分别使用专用字段: articleInfo.title, commentDetail.content
        NotificationResponse.TargetInfo targetInfo = new NotificationResponse.TargetInfo();
        targetInfo.setId(targetId);
        targetInfo.setType(targetType);

        if ("article".equals(targetType)) {
            Map<String, Object> article = articleMap.get(targetId);
            if (article != null) {
                // 只设置 URL，标题由 articleInfo.title 提供
                targetInfo.setUrl("/article/" + targetId);
                notification.setTarget(targetInfo);

                // 填充文章信息 (专用字段)
                NotificationResponse.ArticleInfo articleInfo = new NotificationResponse.ArticleInfo();
                articleInfo.setId(targetId);
                articleInfo.setTitle((String) article.get("title"));
                articleInfo.setSummary((String) article.get("summary"));
                articleInfo.setCoverImage((String) article.get("coverImage"));
                articleInfo.setUrl("/article/" + targetId);
                notification.setArticleInfo(articleInfo);
            }
        } else if ("comment".equals(targetType)) {
            Map<String, Object> comment = commentMap.get(targetId);
            if (comment != null) {
                Long articleId = comment.get("articleId") != null ? ((Number) comment.get("articleId")).longValue() : null;
                String articleTitle = (String) comment.get("articleTitle");

                // 只设置 URL，内容由 commentDetail.content 提供
                targetInfo.setUrl(articleId != null ? "/article/" + articleId + "#comment-" + targetId : null);
                notification.setTarget(targetInfo);

                // 填充评论详情 (专用字段)
                fillCommentDetail(notification, comment, commentMap, userMap, userId);

                // 填充文章信息
                if (articleId != null) {
                    NotificationResponse.ArticleInfo articleInfo = new NotificationResponse.ArticleInfo();
                    articleInfo.setId(articleId);
                    articleInfo.setTitle(articleTitle);
                    articleInfo.setUrl("/article/" + articleId);
                    notification.setArticleInfo(articleInfo);
                }
            }
        } else if ("user".equals(targetType)) {
            Map<String, Object> user = userMap.get(targetId);
            if (user != null) {
                // 只设置 URL，用户名由 sender.nickName 提供
                targetInfo.setUrl("/user/" + targetId);
                notification.setTarget(targetInfo);
            }
        }
    }

    /**
     * 填充评论详情信息
     */
    private void fillCommentDetail(NotificationResponse notification,
                                   Map<String, Object> comment,
                                   Map<Long, Map<String, Object>> commentMap,
                                   Map<Long, Map<String, Object>> userMap,
                                   Long userId) {
        NotificationResponse.CommentDetail commentDetail = new NotificationResponse.CommentDetail();
        commentDetail.setId(((Number) comment.get("id")).longValue());
        commentDetail.setContent((String) comment.get("content"));
        commentDetail.setCreateTime((java.time.LocalDateTime) comment.get("createTime"));
        commentDetail.setLikeCount(comment.get("likeCount") != null ? ((Number) comment.get("likeCount")).intValue() : 0);
        commentDetail.setReplyCount(comment.get("replyCount") != null ? ((Number) comment.get("replyCount")).intValue() : 0);
        commentDetail.setIsLiked(comment.get("isLiked") != null ? ((Number) comment.get("isLiked")).intValue() == 1 : false);

        // 填充评论者信息
        Long commentUserId = comment.get("userId") != null ? ((Number) comment.get("userId")).longValue() : null;
        if (commentUserId != null && userMap.containsKey(commentUserId)) {
            Map<String, Object> commenter = userMap.get(commentUserId);
            NotificationResponse.UserInfo commenterInfo = new NotificationResponse.UserInfo();
            commenterInfo.setUserId(commentUserId);
            commenterInfo.setNickName((String) commenter.get("nickName"));
            commenterInfo.setAvatar((String) commenter.get("avatar"));
            commentDetail.setCommenter(commenterInfo);
        }

        // 填充回复的用户信息
        Long replyToUserId = comment.get("replyToUserId") != null ? ((Number) comment.get("replyToUserId")).longValue() : null;
        if (replyToUserId != null && userMap.containsKey(replyToUserId)) {
            Map<String, Object> replyToUser = userMap.get(replyToUserId);
            NotificationResponse.UserInfo replyToUserInfo = new NotificationResponse.UserInfo();
            replyToUserInfo.setUserId(replyToUserId);
            replyToUserInfo.setNickName((String) replyToUser.get("nickName"));
            replyToUserInfo.setAvatar((String) replyToUser.get("avatar"));
            commentDetail.setReplyToUser(replyToUserInfo);
        }

        notification.setCommentDetail(commentDetail);
        
        // 对于REPLY类型，填充原始评论信息（被回复的评论）
        if ("REPLY".equals(notification.getType())) {
            Object parentIdObj = comment.get("parentId");
            if (parentIdObj != null) {
                Long parentId = ((Number) parentIdObj).longValue();
                if (parentId > 0 && commentMap.containsKey(parentId)) {
                    Map<String, Object> parentComment = commentMap.get(parentId);
                    
                    NotificationResponse.CommentQuote originalComment = new NotificationResponse.CommentQuote();
                    originalComment.setId(parentId);
                    originalComment.setContent((String) parentComment.get("content"));
                    originalComment.setCreateTime((java.time.LocalDateTime) parentComment.get("createTime"));
                    
                    // 填充原评论者信息
                    Long parentAuthorId = parentComment.get("userId") != null ? 
                        ((Number) parentComment.get("userId")).longValue() : null;
                    if (parentAuthorId != null && userMap.containsKey(parentAuthorId)) {
                        Map<String, Object> parentAuthor = userMap.get(parentAuthorId);
                        NotificationResponse.UserInfo parentAuthorInfo = new NotificationResponse.UserInfo();
                        parentAuthorInfo.setUserId(parentAuthorId);
                        parentAuthorInfo.setNickName((String) parentAuthor.get("nickName"));
                        parentAuthorInfo.setAvatar((String) parentAuthor.get("avatar"));
                        originalComment.setCommenter(parentAuthorInfo);
                    }
                    
                    notification.setOriginalComment(originalComment);
                }
            }
        }
    }

    /**
     * 获取操作文本
     */
    private String getActionText(String type, String targetType) {
        switch (type) {
            case "FOLLOW":
                return "关注了你";
            case "LIKE":
                if ("article".equals(targetType)) {
                    return "赞了你的文章";
                } else if ("comment".equals(targetType)) {
                    return "赞了你的评论";
                }
                return "点赞";
            case "COLLECT":
                return "收藏了你的文章";
            case "COMMENT":
                return "评论了你的文章";
            case "REPLY":
                return "回复了你的评论";
            case "MENTION":
                return "在评论中提到了你";
            case "SYSTEM":
                return "系统通知";
            default:
                return "";
        }
    }

    /**
     * 获取类型名称
     */
    private String getTypeName(String type) {
        switch (type) {
            case "FOLLOW":
                return "新增粉丝";
            case "LIKE":
                return "点赞";
            case "COLLECT":
                return "收藏";
            case "COMMENT":
                return "评论";
            case "REPLY":
                return "回复";
            case "MENTION":
                return "提及";
            case "SYSTEM":
                return "系统通知";
            default:
                return "未知";
        }
    }
}

